/**
 * $File: TT_Core.cpp
 * $Synopsis: Logical Stuff Implementation
 * $Author: Ted Yin (ymfoi)
 * $Version: alpha 1.0
 * $Date: Sat Nov 27 22:51:56 2010 CST
 */

#include <cstdlib>
#include <cstring>

#include "TT_Core.h"

using namespace TT_Core;


/*********** PosNode ************/
PosNode::PosNode() {next = NULL;}
/*********** PosNode ************/



/*********** PosList ************/
PosList::PosList() {head = NULL;}

void PosList::Add(int x,int y) {

        PosNode *t = new PosNode;
        t -> next = head;
        t -> x = x;
        t -> y = y;
        head = t;
}

PosList::~PosList() {

        for (PosNode *now = head,*nptr; now;)
        {
                nptr = now -> next;     
                delete now;
                now = nptr;
        }
}
/*********** PosList ************/



/*********** PlayerNode ************/
PlayerNode::PlayerNode() {next = prev = NULL; IsDead = 0; ds = 0; Alpha = 1;}
/*********** PlayerNode ************/



#define IN_RANGE(x,y) (0 <= (x) && (x) < Height && 0 <= (y) && (y) < Width)
/*********** PlayerList ************/
PlayerList::PlayerList() {

        head = NULL; Top = Recycle; ID_Cnt = 0;
        memset(Hash,0,sizeof(Hash)); //init to NULL
}

PlayerList::~PlayerList() {

        for (PlayerNode *now = head,*nptr; now;) 
        {
                nptr = now -> next;
                delete now;
                now = nptr;
        }
}

int PlayerList::Add(int x,int y,int Dir,int InitSpeed) {

        PlayerNode *t = new PlayerNode;
        t -> next = head;
        t -> prev = NULL;
        if (head) head -> prev = t;
        head = t;

        int ID = Top == Recycle ? ++ID_Cnt : *(Top--);
        Hash[ID] = t;

        t -> Speed = (double)InitSpeed / SPEED_PRECISION / 10;
        t -> x = x;
        t -> y = y;
        t -> Now_Dir = Dir;
        t -> ID = ID;


        return ID;
}

void PlayerList::Del(int ID) {

        PlayerNode *t = Hash[ID];

        if (t -> prev) t -> prev -> next = t -> next;
        if (t -> next) t -> next -> prev = t -> prev;
        if (head == t) head = t -> next;
        delete t; //free it
        *(++Top) = ID; //return the ID to recycler
        Hash[ID] = NULL;
}

PlayerNode* PlayerList::GetInfo(int ID) {return Hash[ID];}
/*********** PlayerList ************/



/*********** Arena ************/
Arena::Arena(int H,int W) {

        Height = H,Width = W;
        Map = new int* [Height];
        for (int i = 0; i < Width; i++) Map[i] = new int[Width];
        Dead_Head = Dead;
}

void Arena::InitMatch() { //Call this before the Round begins

        for (int i = 0; i < Height; i++)
                for (int j = 0; j < Width; j++)
                        Map[i][j] = 0;
        Dead_Ptr = Dead;
        if (Players) delete Players;
        Players = new PlayerList;
        Player_Head = Players -> head;
}

int Arena::AddPlayer(int x,int y,int Dir,int InitSpeed,UCHAR R,UCHAR G,UCHAR B) { //Add a Player and return the new player's ID

        if (!IN_RANGE(x,y)) {Err_Num = ERR_NUM_POS_INVALID; return -1;}
        if (Dir > 3 || Dir < 0) {Err_Num = ERR_NUM_DIR_INVALID; return -1;}

        if (Map[x][y]) {Err_Num = ERR_NUM_POS_OCCUPIED; return -1;}
        int ID = Map[x][y] = Players -> Add(x,y,Dir,InitSpeed);
        PlayerNode *t = Players -> GetInfo(ID);
        t -> Route.Add(x,y);
        t -> Red = R;
        t -> Green = G;
        t -> Blue = B;
        Player_Num++;
        return ID;
}

bool Arena::DelPlayer(int ID) { //Delete a Player 

        PlayerNode *t = ID > 0 && ID < MAXID ? Players -> GetInfo(ID) : NULL;

        if (t == NULL) {Err_Num = ERR_NUM_PLAYER_NOT_FOUND; return 0;}  

        for (PosNode *now = t -> Route.head,*nptr; now;)
        {
                nptr = now -> next;
                Map[now -> x][now -> y] = 0; //erase the route
                delete now; //free this PosNode
                now = nptr;
        }
        t -> Route.head = NULL;
        Player_Num--;
        Players -> Del(ID);

        return 1;
}

#define EPS 1e-6

void Arena::IdleMoveReal(Arena *Real) { //All players move in their current directions in Delta Time

        static const int Det[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};
        /*      0
         *      ^
         * 3 <-   -> 1   
         *      v
         *      2 
         */    

        PlayerList *&rPlayers = Real -> Players;
        int **rMap = Real -> Map;
        int Height = Real -> Height,Width = Real -> Width;
        PlayerNode **rDead_Ptr = Real -> Dead_Ptr;

        int id;

        const PlayerNode *t;
        bool ok;

        for (PlayerNode *now = rPlayers -> head; now; now = now -> next)
                if (!now -> IsDead)
                {
                        if ((now -> ds += now -> Speed) >= 1 - EPS)
                        {
                                now -> ds--;
                                int &nd = now -> Now_Dir;
                                int nx = now -> x,ny = now -> y;
                                ok = 1; //go through the body
                                nx += Det[nd][0];
                                ny += Det[nd][1];
                                if (!(IN_RANGE(nx,ny)) || (id = rMap[nx][ny])) 
                                {
                                        const PlayerNode *t = rPlayers -> Hash[id];
                                        if (!t || !t -> IsDead)
                                        {          
                                                *(++rDead_Ptr) = now; 
                                                now -> IsDead = 1;
                                                ok = 0;
                                        }

                                }//Hit the Block
                                if (ok && IN_RANGE(nx,ny))
                                {
                                        now -> x = nx;
                                        now -> y = ny;
                                        now -> Route.Add(nx,ny);
                                        //Update the position
                                }
                                if (IN_RANGE(nx,ny)) rMap[nx][ny] = now -> ID; // if this player is Dead,leave a mark to let the player who comes here in the meantime die as well.
                        }
                }
        for (PlayerNode *now = rPlayers -> head; now; now = now -> next)
                if (!now -> IsDead)
                {
                        if ((id = rMap[now -> x][now -> y]) && id != now -> ID) //check the mark
                        {
                                t = rPlayers -> Hash[id];
                                if (!t -> IsDead)
                                {
                                        *(++rDead_Ptr) = now;
                                        now -> IsDead = 1;
                                }
                        }
                }
                else now -> Alpha -= 0.0003;

}

bool Arena::FlushPlayer(int ID,int ActType) {

        static const int DirChange[4][2] = {{3,1},{0,2},{1,3},{2,0}};

        PlayerNode *t = ID > 0 && ID < MAXID ? Players -> GetInfo(ID) : NULL;
        if (t == NULL) {Err_Num = ERR_NUM_PLAYER_NOT_FOUND; return 0;}  
        if (t -> IsDead) {Err_Num = ERR_NUM_PLAYER_NOT_ALIVE; return 0;}

        switch (ActType) 
        {
                case ACT_TYPE_LEFT : t -> Now_Dir = DirChange[t -> Now_Dir][0]; break;
                case ACT_TYPE_RIGHT : t -> Now_Dir = DirChange[t -> Now_Dir][1]; break;
                case ACT_TYPE_SPEEDUP : if (t -> Speed < MAX_SPEED)  t -> Speed += 0.005; break;
                case ACT_TYPE_SPEEDDOWN : if (t -> Speed > 0.001) t -> Speed -= 0.005; break;
                default : Err_Num = ERR_NUM_ACT_INVALID; return 0;
        }
        return 1;
}

const int** Arena::GetMap() {

        return (const int **)Map;
}

const PlayerNode* Arena::GetPlayer(int ID) {

        if (ID <= 0 && ID >= MAXID) {Err_Num = ERR_NUM_PLAYER_NOT_FOUND; return 0;}
        return Players -> GetInfo(ID);
}

const PlayerNode *Arena::GetPlayerIterator() {

        return Players -> head;
}

/*********** Arena ************/


/*********** ObjectNode ************/
ObjectNode::ObjectNode() {next = prev = NULL; State = OBJ_STATE_RUNNING;}
/*********** ObjectNode ************/


/*********** AnimaStage ************/

AnimaStage::AnimaStage() {

        head = NULL; Top = Recycle; ID_Cnt = 0;
        memset(Hash,0,sizeof(Hash)); //init to NULL
}

AnimaStage::~AnimaStage() {

        for (ObjectNode *now = head,*nptr; now;) 
        {
                nptr = now -> next;
                delete now;
                now = nptr;
        }
}

int AnimaStage::AddObject(void (*func)(void *),void *Real) {

        ObjectNode *t = new ObjectNode;
        t -> next = head;
        t -> prev = NULL;
        if (head) head -> prev = t;
        head = t;

        int ID = Top == Recycle ? ++ID_Cnt : *(Top--);
        Hash[ID] = t;
        t -> ptr = func;
        t -> Real = Real;
        t -> ID = ID;

        return ID;
}

void AnimaStage::DelObject(ObjectNode *t) {

        if (t -> prev) t -> prev -> next = t -> next;
        if (t -> next) t -> next -> prev = t -> prev;
        if (head == t) head = t -> next;
        int ID = t -> ID;
        delete t; //free it
        *(++Top) = ID; //return the ID to recycler
        Hash[ID] = NULL;

}

void AnimaStage::MoveOn(double DetTime) {

        int Total_time_unit = DetTime * SPEED_PRECISION;
        for (int t = 1; t <= Total_time_unit; t++)
        {
                for (ObjectNode *now = head; now; now = now -> next)
                        if (now -> State == OBJ_STATE_RUNNING) (now -> ptr)(now -> Real);
                        else if (now -> State == OBJ_STATE_EXIT)
                        {
                                ObjectNode *tmp = now;
                                now = now -> prev;
                                DelObject(tmp);
                        }
        }
}

bool AnimaStage::SendSignal(int Sig,int ID) {

        ObjectNode *t = Hash[ID];
        if (t == NULL) {Err_Num = ERR_NUM_OBJ_NOT_FOUND; return 0;}
        
        t -> State = Sig;
        return 1;
}

/*********** AnimaStage ************/

